<script setup>
	import { onLoad, onUnload } from '@dcloudio/uni-app'
	import { reactive, ref, watch, getCurrentInstance, onMounted, nextTick } from "vue"
	import { SpeechTranscription } from '@/voice/st.js'
	import { getAliTokenAPI } from '@/api/index.js'
	import { aliyunUrl, appKey, uploadUrl } from "@/common/config.js"
	import { chatbotMessageStore } from "@/stores/chatbotMessage.js"
	import { inProgressStore } from "@/stores/inProgress.js"
	import { useUserStore } from "@/stores/user.js"
	const instance = getCurrentInstance()
	const chatbotMessage = chatbotMessageStore()
	const inProgress = inProgressStore()
	const userStore = useUserStore()

	// 声波动画数据
	const barData = [
		'1s', '0.9s', '0.8s', '0.7s', '0.6s', '0.5s', '0.4s', '0.3s', '0.2s', '0.1s',
		'1s', '0.9s', '0.8s', '0.7s', '0.6s', '0.5s', '0.4s', '0.3s', '0.2s', '0.1s',
	]
	// 切换键盘输入还是语音合成
	const show = ref(true)
	// 存储textarea的属性
	const textareaValue = reactive({
		autoHeight: true,
		alignItems: 'center',
		height: '0px'
	})
	// 输入框换行时触发
	const onLineChange = (event) => {
		const { height, lineCount } = event.detail
		// 如果>1行
		textareaValue.alignItems = lineCount > 1 ? 'flex-end' : 'center'
		// 如果>5行，不再自动增高
		if (lineCount > 5) {
			textareaValue.autoHeight = false
			textareaValue.height = height
		} else {
			textareaValue.autoHeight = true
		}
	}
	// textarea的父级高度
	const textareaHeight = ref('')
	// 获取textarea的父级高度
	onMounted(() => {
		const query = uni.createSelectorQuery().in(instance.proxy);
		query.select('.input-content').boundingClientRect((rect) => {
			textareaHeight.value = rect.height + 'px'
		}).exec()
	})

	// 显示隐藏语音录制区域
	const showAudio = ref(false)
	// 输入框的值
	const inputContent = ref('')
	// 获取全局唯一的录音管理器
	const recorderManager = wx.getRecorderManager()
	// 阿里云实例化的类
	const launckVoice = ref(null)
	// 存储语音识别结果
	const storageArr = ref([])

	// 向服务端发送
	const sendIng = () => {
		if (inProgress.queryStatus()) return
		if (inputContent.value.trim() === '') return
		chatbotMessage.startSending(inputContent.value.trim())
		// 发送完毕清空输入框
		inputContent.value = ''
		// // 清空录音的结果
		storageArr.value = []
	}

	// 手指长按开始说话
	const longpress = async () => {
		console.log('长按开始');
		if (inProgress.queryStatus()) return
		showAudio.value = true
		await launckVoice.value.start(launckVoice.value.defaultStartParams())
		recorderManager.start({
			duration: 100000,
			sampleRate: 16000,
			numberOfChannels: 1,
			format: 'PCM',
			frameSize: 4
		})
	}
	// 手指放开
	const touchend = () => {
		console.log('手指放开');
		showAudio.value = false
		recorderManager.stop()
	}
	// 实时输出录音
	recorderManager.onFrameRecorded(res => {
		console.log(res);
		launckVoice.value.sendAudio(res.frameBuffer)
	})
	// 监听录音结束
	recorderManager.onStop(res => {
		console.log('录音结束了');
		console.log(res);
		showAudio.value = false
		// 强制关闭阿里云语音识别监听
		launckVoice.value.shutdown()
		// 录制结束取出文字发送大模型
		if (storageArr.value.length > 0) {
			storageArr.value.forEach(item => {
				inputContent.value += item.result
			})
			setTimeout(() => {
				sendIng()
			}, 400)
		}
	})
	// 监听录音错误事件
	recorderManager.onError(res => {
		showAudio.value = false
		const typeData = [{
				type: 'operateRecorder:fail auth deny',
				text: '右上角里设置里打开麦克风'
			},
			{
				type: 'operateRecorder:fail NotFoundError',
				text: '请打开麦克风才可以说话'
			},
		]
		typeData.forEach(item => {
			if (res.errMsg == item.type) {
				uni.showToast({ icon: 'none', title: item.text })
			}
		})
		// 强制关闭阿里云语音识别监听
		launckVoice.value.shutdown()
	})

	// ---------------------阿里云语音识别---------------------
	// 初始化阿里云语音识别
	let st = null
	const initST = async () => {
		const { data } = await getAliTokenAPI()
		st = new SpeechTranscription({
			url: aliyunUrl,
			token: data,
			appkey: appKey
		})
		launckVoice.value = st
		// 实时语音识别开始。
		st.on("started", () => {
			console.log('实时语音识别开始');
		})
		// 实时语音识别中间结果。
		st.on("changed", msg => {
			console.log('实时语音识别中间结果');
			console.log(msg);
			const res = JSON.parse(msg)
			const queryIndex = storageArr.value.findIndex(item => item.index === res.payload.index)
			if (queryIndex >= 0) {
				storageArr.value[queryIndex].result = res.payload.result
			} else {
				storageArr.value.push(res.payload)
			}
		})
		// 提示句子结束。
		st.on("end", msg => {
			console.log('提示句子结束');
			console.log(msg);
			const res = JSON.parse(msg)
			const queryIndex = storageArr.value.findIndex(item => item.index === res.payload.index)
			if (queryIndex >= 0) {
				storageArr.value[queryIndex].result = res.payload.result
			} else {
				storageArr.value.push(res.payload)
			}
		})
		// 连接关闭。
		st.on("closed", () => {
			console.log('连接关闭');
		})
		// 错误。
		st.on("failed", (err) => {
			console.log('阿里云语音识别错误');
			console.log(err);
			uni.showToast({
				icon: "none",
				title: '录音出现错误'
			})
		})
	}
	// 监听用户token
	watch(() => userStore.userInfo.token, (newVal) => {
		if (newVal && !st) {
			initST()
		}
	})

	onLoad(() => {
		if (userStore.userInfo.token) {
			initST()
		}
		// 监听键盘高度变化
		uni.onKeyboardHeightChange(res => {
			if (res.height) {
				keyboardHeight.value = res.height;
			} else {
				keyboardHeight.value = 0 // 键盘收起时重置高度
			}
		});
	})

	// 键盘高度
	const keyboardHeight = ref(0) // 初始化键盘的高度

	onUnload(() => {
		// 页面卸载时移除监听
		uni.offKeyboardHeightChange()
	})
</script>

<template>
	<!-- 文字输入 -->
	<view class="input-box-area" :style="{bottom:keyboardHeight+'px'}">
		<image @click="show = !show" :src="show ? '/static/images/yuyin.png' : '/static/images/jianpan.png'"
			mode="widthFix">
		</image>
		<view class="input-content" v-show="show">
			<textarea class="textarea-box" v-model="inputContent" placeholder="你有什么想知道的,快来问我" :show-confirm-bar="false"
				maxlength="500" cursor-spacing="20" :adjust-position="false" fixed :auto-height="textareaValue.autoHeight"
				@linechange="onLineChange"></textarea>
		</view>
		<view class="speech-sound" v-show="!show" @longpress="longpress" @touchend="touchend">按住 说话</view>
		<image src="/static/images/fasong.png" mode="widthFix" @click="sendIng"></image>
	</view>
	<!-- 录制语音弹窗 -->
	<view class="mask-view" v-if="showAudio"></view>
	<view class="record-text" v-if="showAudio">
		{{storageArr.map(item=>item.result).join(' ')}}
	</view>
	<view class="recording-pop-up" v-if="showAudio">
		<text class="release">松开 发送</text>
		<text class="in-recognition">正在识别声音...</text>
		<!-- 动画 -->
		<view class="audio-wave">
			<text class="audio-wave-text" v-for="(item,index) in barData" :key="index"
				:style="{'animationDelay':item}"></text>
		</view>
	</view>
</template>

<style lang="scss" scoped>
	.input-box-area {
		background-color: #F8F8F8;
		position: fixed;
		left: 0;
		right: 0;
		bottom: 0;
		display: flex;
		align-items: flex-end;
		padding-bottom: calc(30rpx + env(safe-area-inset-bottom));
		padding-top: 30rpx;

		image {
			width: 60rpx;
			height: 60rpx;
			margin: 0 20rpx;
		}

		.input-content {
			background-color: #ffffff;
			flex: 1;
			width: 100%;
			border-radius: 15rpx;
			padding: 10rpx;
			min-height: 60rpx;

			.textarea-box {
				width: 100%;
				height: v-bind('textareaValue.height');
			}
		}

		.speech-sound {
			flex: 1;
			background: linear-gradient(to right, #A2C5FE, #C0E7FD);
			text-align: center;
			border-radius: 15rpx;
			color: #fff;
			height: v-bind('textareaHeight');
			line-height: v-bind('textareaHeight');
		}
	}

	// 录制语音遮罩层
	.mask-view {
		position: fixed;
		top: 0;
		left: 0;
		right: 0;
		bottom: 0;
		background-color: rgba(0, 0, 0, 0.7);
		z-index: 100;
	}

	// 语音转换的文字
	.record-text {
		position: fixed;
		bottom: 500rpx;
		left: 10rpx;
		right: 10rpx;
		color: #ffffff;
		height: 300rpx;
		line-height: 1.4;
		overflow: auto;
		padding: 10rpx;
		z-index: 100;
	}

	// 录制区域
	.recording-pop-up {
		position: fixed;
		bottom: 0;
		left: 0;
		right: 0;
		background: linear-gradient(to right, #07D280, #16CBDC);
		height: 500rpx;
		z-index: 100;
		display: flex;
		flex-direction: column;
		align-items: center;

		.release {
			color: #ffffff;
			font-size: 33rpx;
			padding: 30rpx 0;
			font-weight: bold;
		}

		.in-recognition {
			color: #ffffff;
			font-size: 40rpx;
			padding: 30rpx 0;
			font-weight: bold;
		}

		// 声波效果
		.audio-wave {
			padding-top: 50rpx;

			.audio-wave-text {
				background-color: #ffffff;
				width: 7rpx;
				height: 10rpx;
				margin: 0 5rpx;
				border-radius: 5rpx;
				display: inline-block;
				border: none;
				animation: wave 0.2s ease-in-out;
				animation-iteration-count: infinite;
				animation-direction: alternate;
			}
		}
	}

	@keyframes wave {
		from {
			transform: scaleY(1);
		}

		to {
			transform: scaleY(4);
		}
	}
</style>